use std::io::{Read, Write};
use std::net::TcpStream;

use log::{debug, error, info, trace};

use crate::smtp::auth::Credential;
use crate::smtp::mail::GenerateMailMsg;
use crate::utils::*;

///`Mailer`邮件发送器结构体，封装发送邮件所需的数据和方法
/// # Example
/// ```
/// let mailer = Mailer::new()
///     .set_server_address("smtp.126.com")
///     .set_port(25);//default 25
///     .add_receiver("addr10@qq.com")
///     .add_receiver("addr2@126.com")
///
/// ```
#[derive(Debug)]
pub struct Mailer {
    server_address: String,
    server_port: u16,
    receiver_addr: Vec<String>,
    sender_addr: String,
    mail_content: String,
    credit: Option<Credential>,
}

// 发送器的初始化与设置函数
impl Mailer {
    pub fn new() -> Mailer {
        Mailer {
            server_address: "".to_string(),
            server_port: 25,
            receiver_addr: Vec::new(),
            sender_addr: "".to_string(),
            mail_content: "".to_string(),
            credit: None,
        }
    }
    /// 设置服务器的地址，hostname 或 IpAddr，必填
    /// # Example
    /// ```
    /// let mailer = Mailer::new()
    ///             .set_server_address("smtp.126.com");
    /// ```
    pub fn set_server_address(mut self, address: &str) -> Self {
        self.server_address = address.to_string();
        self
    }
    /// 设置服务器的端口，非必填，默认25
    pub fn set_port(mut self, port: u16) -> Self {
        self.server_port = port;
        self
    }
    /// 设置发送者，非必填，可为匿名邮件
    pub fn set_sender(mut self, sender: &str) -> Self {
        self.sender_addr = sender.to_string();
        self
    }
    /// 添加接受者的邮箱地址，必填，可添加多个,逗号隔开
    pub fn add_receiver(mut self, new_receiver: &str) -> Self {
        for r in new_receiver.split(",") {
            self.receiver_addr.push(format!("<{}>", r));
        }
        self
    }
    /// 设置邮件内容，接受实现`GenerateMailMsg`特征的邮件，必填
    pub fn set_mail<T: GenerateMailMsg>(mut self, mail: T) -> Self {
        self.mail_content = mail.generate_mail_msg();
        self
    }
    /// 通过字符直接设置内容，适用于转发
    // pub fn set_mail_str(mut self, mail: &str) -> Self {
    //     self.mail_content = mail.to_string();
    //     self
    // }
    /// 转发邮件时登录使用，传入`Credential`实例
    /// # Example
    /// ```
    /// let credit = Credential::new("NAME", "AAAAACGGGNS");
    /// Mailer::new().auth(credit);
    /// ```
    /// 实际发送中使用身份验证发送邮件为‘转发’，
    /// 非转发会被服务器拒绝
    /// # Example
    /// 在向126的邮件服务器接受发送邮件时会出现
    /// ```shell
    /// 553 Local user only,126 zwqz-smtp-mta-g2-1,_____wCnlVREteBjaxqPAg--.3931S2 1675670880
    /// ```
    /// 如果直接向nslookup查出的邮件交换服务器（126mx01.mxmail.netease.com）发送则会
    /// ```shell
    /// 550 DI:SPF 126 mx42,O+RpCgB3pSzto+BjhpaVAQ--.54248S2 1675666413,please see http://mail.163.com/help/help_spam_16.htm?ip=27.200.1
    /// ```
    pub fn auth(mut self, credit: Credential) -> Self {
        self.credit = Option::from(credit);
        self
    }
}

// 发送所需的步骤
impl Mailer {
    // 检查所有信息是否设置完整
    fn check(&self) -> Result<(), Error> {
        trace!("Mailer sets:{:?}",self);
        return if self.server_address.is_empty() || self.receiver_addr.is_empty() || self.mail_content.is_empty() {
            error!("Mailer have NOT been completely initialized!");
            Err(Error::SmtpError(SmtpError::InvalidInput))
        } else { Ok(()) };
    }
    //检查回复是否为合法，`legal_code`为合法的状态码，如250,220,443
    fn response_status(buffer: &[u8; 128], legal_code: &str) -> Result<String, ()> {
        let msg = String::from_utf8_lossy(&buffer[..]);
        return if &msg[0..3] == legal_code {
            debug!("[Serv] {}",msg.trim_end_matches(|c|c=='\0'|| c=='\r' || c=='\n'));
            Ok(msg.parse().unwrap())
        } else {
            error!("{}",msg.trim_end_matches(|c|c=='\0'|| c=='\r' || c=='\n'));
            Err(())
        };
    }

    // 连接并HELO
    fn connect(&self) -> Result<TcpStream, Error> {
        let mut buffer = [0; 128];
        let mut stream = TcpStream::connect((self.server_address.clone(), self.server_port))?;
        info!("Connect SMTP Server Success.");
        stream.read(&mut buffer)?;
        match Self::response_status(&buffer, "220") {
            Ok(_) => {}
            Err(_) => { return Err(Error::SmtpError(SmtpError::InvalidAddress)); }
        };
        stream.write("HELO Test\r\n".as_ref())?;
        debug!("[User]HELO Test\r\n");
        buffer = [0; 128];
        stream.read(&mut buffer)?;
        match Self::response_status(&buffer, "250") {
            Ok(_) => {}
            Err(_) => { return Err(Error::SmtpError(SmtpError::EHLO)); }
        };
        Ok(stream)
    }
    // 验证登录
    fn auth_login(&self, mut stream: TcpStream) -> Result<TcpStream, Error> {
        let mut buffer = [0; 128];
        trace!("Auth Login...");
        stream.write("AUTH LOGIN\r\n".as_bytes())?;
        debug!("[User]AUTH LOGIN\r\n");
        stream.read(&mut buffer)?;
        match Self::response_status(&buffer, "334") {
            Ok(_) => {}
            Err(_) => { return Err(Error::SmtpError(SmtpError::AuthError)); }
        };

        let (username, password) = self.credit.as_ref().unwrap().base64();
        trace!("username:{},Password:{}",username,password);

        trace!("Username Checking");
        stream.write(username.as_bytes())?;
        debug!("[User] {}\r\n",username);
        buffer = [0; 128];
        stream.read(&mut buffer)?;
        match Self::response_status(&buffer, "334") {
            Ok(_) => {}
            Err(_) => { return Err(Error::SmtpError(SmtpError::AuthError)); }
        };

        trace!("Password Checking");
        stream.write(password.as_bytes())?;
        debug!("[User] {}",password);
        buffer = [0; 128];
        stream.read(&mut buffer)?;
        match Self::response_status(&buffer, "235") {
            Ok(_) => {}
            Err(_) => { return Err(Error::SmtpError(SmtpError::AuthError)); }
        };
        Ok(stream)
    }
    fn send_data(&self, mut stream: TcpStream) -> Result<TcpStream, Error> {
        let mut buffer = [0; 128];
        let mail_from = format!("MAIL FROM: <{}>\r\n", self.sender_addr);

        // mail from
        trace!("Mail From...");
        stream.write(mail_from.as_bytes())?;
        debug!("[User] {}",mail_from);
        stream.read(&mut buffer)?;
        match Self::response_status(&buffer, "250") {
            Ok(_) => {}
            Err(_) => { return Err(Error::SmtpError(SmtpError::MailFrom)); }
        };


        //rcpt to
        for receiver in &self.receiver_addr {
            let rcpt_to = format!("RCPT TO:{}\r\n", receiver);
            trace!("Rcpt To.{}..",receiver);
            stream.write(rcpt_to.as_bytes())?;
            debug!("[User] {}",rcpt_to);
            buffer = [0; 128];
            stream.read(&mut buffer)?;
            match Self::response_status(&buffer, "250") {
                Ok(_) => {}
                Err(_) => { return Err(Error::SmtpError(SmtpError::RcptTo(receiver.to_string()))); }
            };
        }

        //data
        trace!("Data requesting...");
        stream.write("DATA\r\n".as_bytes())?;
        debug!("[User] DATA");
        buffer = [0; 128];
        stream.read(&mut buffer)?;
        match Self::response_status(&buffer, "354") {
            Ok(_) => {}
            Err(_) => { return Err(Error::SmtpError(SmtpError::Data)); }
        };

        //mail
        info!("Mail sending...");
        let mail = self.mail_content.clone();
        trace!("{}",mail);
        stream.write(mail.as_bytes())?;
        debug!("[User] {}",mail);
        buffer = [0; 128];
        stream.read(&mut buffer)?;
        match Self::response_status(&buffer, "250") {
            Ok(_) => {}
            Err(_) => { return Err(Error::SmtpError(SmtpError::Data)); }
        };
        info!("Send success");
        Ok(stream)
    }
    /// 设置好后发送邮件
    pub fn send(&self) -> Result<(), Error> {
        info!("Mail Sending...");
        self.check()?;
        let mut stream = self.connect()?;
        match &self.credit {
            None => {
                return Err(Error::SmtpError(SmtpError::InvalidInput));
            }
            Some(_) => {
                stream = self.auth_login(stream)?;
            }
        }
        stream = self.send_data(stream)?;

        debug!("[User] QUIT");
        stream.write("QUIT\r\n".as_bytes())?;
        Ok(())
    }
}